// Fill out your copyright notice in the Description page of Project Settings.


#include "ModelBrowserPawn.h"

#include "Camera/CameraComponent.h"
#include "Components/InputComponent.h"
#include "Components/SphereComponent.h"
#include "GameFramework/FloatingPawnMovement.h"
#include "GameFramework/PlayerInput.h"
#include "GameFramework/SpringArmComponent.h"
#include "Engine/CollisionProfile.h"
#include "GameFramework/PlayerController.h"
#include "UObject/ConstructorHelpers.h"
// Sets default values

FName AModelBrowserPawn::MovementComponentName(TEXT("MovementComponent0"));
FName AModelBrowserPawn::CollisionComponentName(TEXT("CollisionComponent0"));
FName AModelBrowserPawn::MeshComponentName(TEXT("MeshComponent0"));


AModelBrowserPawn::AModelBrowserPawn(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	SetCanBeDamaged(true);

	SetRemoteRoleForBackwardsCompat(ROLE_SimulatedProxy);
	bReplicates = true;
	NetPriority = 3.0f;
	speed = 5; //�ƶ��ٶ�
	BaseEyeHeight = 0.0f;
	bCollideWhenPlacing = false;

	SpawnCollisionHandlingMethod = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; //actor��ָ��λ�����ɲ�������ײ

	//CollisionComponent//��ײ�������һЩ��ײ����
	CollisionComponent = CreateDefaultSubobject<USphereComponent>(AModelBrowserPawn::CollisionComponentName);
	CollisionComponent->InitSphereRadius(35.0f);
	CollisionComponent->SetCollisionProfileName(UCollisionProfile::Pawn_ProfileName);
	CollisionComponent->CanCharacterStepUpOn = ECB_No;
	CollisionComponent->SetShouldUpdatePhysicsVolume(true);
	CollisionComponent->SetCanEverAffectNavigation(false);
	CollisionComponent->bDynamicObstacle = true;
	//��ֵ��ײ���
	RootComponent = CollisionComponent;

	MovementComponent = CreateDefaultSubobject<UPawnMovementComponent, UFloatingPawnMovement>(
		AModelBrowserPawn::MovementComponentName);
	MovementComponent->UpdatedComponent = CollisionComponent;
	// Structure to hold one-time initialization
	struct FConstructorStatics
	{
		ConstructorHelpers::FObjectFinder<UStaticMesh> SphereMesh;

		FConstructorStatics()
			: SphereMesh(TEXT("/Engine/EngineMeshes/Sphere"))
		{
		}
	};

	static FConstructorStatics ConstructorStatics;

	if (MeshComponent)
	{
		MeshComponent->SetStaticMesh(ConstructorStatics.SphereMesh.Object);
		MeshComponent->AlwaysLoadOnClient = true;
		MeshComponent->AlwaysLoadOnServer = true;
		MeshComponent->bOwnerNoSee = true;
		MeshComponent->bCastDynamicShadow = true;
		MeshComponent->bAffectDynamicIndirectLighting = false;
		MeshComponent->bAffectDistanceFieldLighting = false;
		MeshComponent->bVisibleInRayTracing = false;
		MeshComponent->PrimaryComponentTick.TickGroup = TG_PrePhysics;
		MeshComponent->SetupAttachment(RootComponent);
		MeshComponent->SetCollisionProfileName(UCollisionProfile::Pawn_ProfileName);
		const float Scale = CollisionComponent->GetUnscaledSphereRadius() / 160.f;
		// @TODO: hardcoding known size of EngineMeshes.Sphere. Should use a unit sphere instead.
		MeshComponent->SetRelativeScale3D(FVector(Scale));
		MeshComponent->SetGenerateOverlapEvents(false);
		MeshComponent->SetCanEverAffectNavigation(false);
	}

	// This is the default pawn class, we want to have it be able to move out of the box.
	bAddDefaultMovementBindings = true;

	BaseTurnRate = 45.f;
	BaseLookUpRate = 45.f;
	MoveCameraToVector(400, 999, 2133);
}

//����Ĭ�ϲ������ֺͶ�Ӧ��ӳ��
void InitializeDefaultPawnInputBindings()
{
	static bool bBindingsAdded = false;
	if (!bBindingsAdded)
	{
		bBindingsAdded = true;

		//����ǰ���ӳ��DefaultPawn_MoveForwardΪ����W��Up
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveForward", EKeys::W, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveForward", EKeys::S, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveForward", EKeys::Up, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveForward", EKeys::Down, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(
			FInputAxisKeyMapping("DefaultPawn_MoveForward", EKeys::Gamepad_LeftY, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveRight", EKeys::A, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveRight", EKeys::D, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(
			FInputAxisKeyMapping("DefaultPawn_MoveRight", EKeys::Gamepad_LeftX, 1.f));

		// HACK: Android controller bindings in ini files seem to not work
		//  Direct overrides here some to work
#if !PLATFORM_ANDROID
		UPlayerInput::AddEngineDefinedAxisMapping(
			FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Gamepad_LeftThumbstick, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(
			FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Gamepad_RightThumbstick, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(
			FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Gamepad_FaceButton_Bottom, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::LeftControl, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::SpaceBar, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::C, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::E, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Q, -1.f));
#else
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Gamepad_LeftTriggerAxis, -0.5f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Gamepad_RightTriggerAxis, 0.5f));
#endif
		UPlayerInput::AddEngineDefinedAxisMapping(
			FInputAxisKeyMapping("DefaultPawn_TurnRate", EKeys::Gamepad_RightX, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_TurnRate", EKeys::Left, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_TurnRate", EKeys::Right, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_Turn", EKeys::MouseX, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(
			FInputAxisKeyMapping("DefaultPawn_LookUpRate", EKeys::Gamepad_RightY, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_LookUp", EKeys::MouseY, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(
			FInputAxisKeyMapping("DefaultPawn_MouseWheel", EKeys::MouseWheelAxis, 1.f));
	}
}

void AModelBrowserPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	check(PlayerInputComponent);

	if (bAddDefaultMovementBindings)
	{
		InitializeDefaultPawnInputBindings();


		//����ӳ��Ͷ�Ӧ���¼�
		PlayerInputComponent->BindAxis("DefaultPawn_MoveForward", this, &AModelBrowserPawn::MoveForward);
		PlayerInputComponent->BindAxis("DefaultPawn_MoveRight", this, &AModelBrowserPawn::MoveRight);
		PlayerInputComponent->BindAxis("DefaultPawn_MoveUp", this, &AModelBrowserPawn::MoveUp_World);
		PlayerInputComponent->BindAxis("DefaultPawn_Turn", this, &AModelBrowserPawn::AddControllerYawInput);
		PlayerInputComponent->BindAxis("DefaultPawn_TurnRate", this, &AModelBrowserPawn::TurnAtRate);
		PlayerInputComponent->BindAxis("DefaultPawn_LookUp", this, &AModelBrowserPawn::AddControllerPitchInput);
		PlayerInputComponent->BindAxis("DefaultPawn_LookUpRate", this, &AModelBrowserPawn::LookUpAtRate);
		PlayerInputComponent->BindAxis("DefaultPawn_MouseWheel", this, &AModelBrowserPawn::MouseWheel);
	}
}


void AModelBrowserPawn::AddControllerRollInput(float Val)
{
	if (Val != 0.f && Controller && Controller->IsLocalPlayerController())
	{
		APlayerController* const PC = CastChecked<APlayerController>(Controller);
		PC->AddRollInput(Val);
	}
}


void AModelBrowserPawn::AddControllerPitchInput(float Val)
{
	if (Val != 0.f && Controller && Controller->IsLocalPlayerController())
	{
		APlayerController* const PC = CastChecked<APlayerController>(Controller);
		PC->AddPitchInput(Val);
	}
}

void AModelBrowserPawn::AddControllerYawInput(float Val)
{
	if (Val != 0.f && Controller && Controller->IsLocalPlayerController())
	{
		APlayerController* const PC = CastChecked<APlayerController>(Controller);
		PC->AddYawInput(Val);
	}
}

void AModelBrowserPawn::TurnAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation);
}

void AModelBrowserPawn::LookUpAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation);
}

void AModelBrowserPawn::UpdateNavigationRelevance()
{
	if (CollisionComponent)
	{
		CollisionComponent->SetCanEverAffectNavigation(bCanAffectNavigationGeneration);
	}
}

//����AD
void AModelBrowserPawn::MoveRight(float Val)
{
	if (Val != 0.f)
	{
		FVector NewLocation = GetActorLocation();
		FRotator const ControlSpaceRot = Controller->GetControlRotation();
		NewLocation += FRotationMatrix(ControlSpaceRot).GetScaledAxis(EAxis::Y) * Val * speed;
		//NewLocation += GetActorRightVector() * Val * speed;
		SetActorLocation(NewLocation);
	}
	/*if (Val != 0.f)
	{
		if (Controller)
		{
			FRotator const ControlSpaceRot = Controller->GetControlRotation();
			// transform to world space and add it
			AddMovementInput( FRotationMatrix(ControlSpaceRot).GetScaledAxis( EAxis::Y ), Val* speed );
		}
	}*/
}

//ǰ��WS
void AModelBrowserPawn::MoveForward(float Val)
{
	if (Val != 0.f)
	{
		FVector NewLocation = GetActorLocation();
		FRotator const ControlSpaceRot = Controller->GetControlRotation();
		//GetActorForwardVector()�����ǻ�ȡ����������X����мӼ�
		//NewLocation += GetActorForwardVector() * Val * speed;
		//FRotationMatrix(ControlSpaceRot).GetScaledAxis( EAxis::X )�ǻ�ȡ��ǰ�ӽ��ж�Ӧ��X����мӼ�
		NewLocation += FRotationMatrix(ControlSpaceRot).GetScaledAxis(EAxis::X) * Val * speed;
		SetActorLocation(NewLocation);
	}
	/*if (Val != 0.f)
	{
		if (Controller)
		{
			FRotator const ControlSpaceRot = Controller->GetControlRotation();
			// transform to world space and add it
			AddMovementInput( FRotationMatrix(ControlSpaceRot).GetScaledAxis( EAxis::X ), Val* speed );
		}
	}*/
}

//����QE
void AModelBrowserPawn::MoveUp_World(float Val)
{
	if (Val != 0.f)
	{
		FVector NewLocation = GetActorLocation();
		FRotator const ControlSpaceRot = Controller->GetControlRotation();
		NewLocation += FRotationMatrix(ControlSpaceRot).GetScaledAxis(EAxis::Z) * Val * speed;
		SetActorLocation(NewLocation);
	}
	/*if (Val != 0.f)
	{
		if (Controller)
		{
			FRotator const ControlSpaceRot = Controller->GetControlRotation();
			// transform to world space and add it
			AddMovementInput( FRotationMatrix(ControlSpaceRot).GetScaledAxis( EAxis::Z ), Val* speed );
		}
	}*/
}


void AModelBrowserPawn::MouseWheel(float Rate)
{
	if (Rate != 0)
	{
		if (Rate > 0)
		{
			speed *= 1.2;
			if (speed > 99999)
			{
				speed = 99999;
			}
		}
		else
		{
			speed /= 1.2;
			if (speed < 1)
			{
				speed = 1;
			}
		}
	}
}

void AModelBrowserPawn::MoveCameraToVector(FVector Vector)
{
	SetActorLocation(Vector);
}

void AModelBrowserPawn::MoveCameraToVector(int x, int y, int z)
{
	FVector Vector = FVector(x, y, z);
	SetActorLocation(Vector);
}

void AModelBrowserPawn::SaveLocationRotation()
{
	FVector const NewLocation = GetActorLocation();
	FString SaveFile = FPaths::ProjectConfigDir() + "Location.ini";
	GConfig->SetString(TEXT("Location"), TEXT("X"), *FString::SanitizeFloat(NewLocation.X), *SaveFile);
	GConfig->SetString(TEXT("Location"), TEXT("Y"), *FString::SanitizeFloat(NewLocation.Y), *SaveFile);
	GConfig->SetString(TEXT("Location"), TEXT("Z"), *FString::SanitizeFloat(NewLocation.Z), *SaveFile);
	if (Controller)
	{
		FRotator const ControlSpaceRot = Controller->GetControlRotation();
		GConfig->SetString(TEXT("Rotation"), TEXT("Pitch"), *FString::SanitizeFloat(ControlSpaceRot.Pitch), *SaveFile);
		GConfig->SetString(TEXT("Rotation"), TEXT("Roll"), *FString::SanitizeFloat(ControlSpaceRot.Roll), *SaveFile);
		GConfig->SetString(TEXT("Rotation"), TEXT("Yaw"), *FString::SanitizeFloat(ControlSpaceRot.Yaw), *SaveFile);
	}
	GConfig->Flush(false, *SaveFile);
}

void AModelBrowserPawn::LoadLocation()
{
	FVector const NewLocation = GetActorLocation();
	FString SaveFile = FPaths::ProjectConfigDir() + "Location.ini";
	float X = 0;
	float Y = 0;
	float Z = 0;
	GConfig->GetFloat(TEXT("Location"), TEXT("X"), X, *SaveFile);
	GConfig->GetFloat(TEXT("Location"), TEXT("Y"), Y, *SaveFile);
	GConfig->GetFloat(TEXT("Location"), TEXT("Z"), Z, *SaveFile);
	SetActorLocation(FVector(X, Y, Z));
	if (Controller)
	{
		float Pitch = 0;
		float Roll = 0;
		float Yaw = 0;
		GConfig->GetFloat(TEXT("Rotation"), TEXT("Pitch"), Pitch, *SaveFile);
		GConfig->GetFloat(TEXT("Rotation"), TEXT("Roll"), Roll, *SaveFile);
		GConfig->GetFloat(TEXT("Rotation"), TEXT("Yaw"), Yaw, *SaveFile);
		Controller->SetControlRotation(FRotator(Pitch, Yaw, Roll));
	}
}


void AModelBrowserPawn::FocusViewportOnBox( const FBox& BoundingBox, bool bInstant /* = false */ )
{
	const FVector Position = BoundingBox.GetCenter();
	float Radius = FMath::Max(BoundingBox.GetExtent().Size(), 10.f);

	float AspectToUse = 1.77777777;
	Radius *= AspectToUse;
	const float HalfFOVRadians = FMath::DegreesToRadians( 90 / 2.0f);
	const float DistanceFromSphere = Radius / FMath::Tan( HalfFOVRadians );
	FVector CameraOffsetVector = Controller->GetControlRotation().Vector() * -DistanceFromSphere;
	SetActorLocation(Position + CameraOffsetVector);
	
}


UPawnMovementComponent* AModelBrowserPawn::GetMovementComponent() const
{
	return MovementComponent;
}
